home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / gdevlxm.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  14.2 KB  |  424 lines

  1. /* Copyright (C) 1989-1994, 1998, 1999 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: gdevlxm.c,v 1.2 2000/09/19 19:00:13 lpd Exp $*/
  20. /*
  21.  * Lexmark 5700 ink-jet printer driver for Ghostscript
  22.  *
  23.  * defines the lxm5700m device for printing in black-and-white at 1200 dpi
  24.  * doesn't handle color or any other resolution.
  25.  * Native resolution appears to be 600 x 1200, but print bands are overlapped.
  26.  *
  27.  * I use the command
  28.  * gs -sOutputFile=/dev/lp0 -sDevice=lxm5700m -dHeadSeparation=15 file.ps
  29.  *
  30.  * where HeadSeparation varies from print-cartridge to print-cartridge and
  31.  * 16 (the default) usually works fine.
  32.  *
  33.  *   Stephen Taylor  setaylor@ma.ultranet.com  staylor@cs.wpi.edu
  34.  */
  35.  
  36. #include "gdevprn.h"
  37. #include "gsparams.h"
  38.  
  39. /* The procedure descriptors */
  40. /* declare functions */
  41. private dev_proc_print_page(lxm5700m_print_page);
  42. private dev_proc_get_params(lxm_get_params);
  43. private dev_proc_put_params(lxm_put_params);
  44.  
  45. /* set up dispatch table.  I follow gdevdjet in using gdev_prn_output_page */
  46. static const gx_device_procs lxm5700m_procs = 
  47.     prn_params_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
  48.                      lxm_get_params, lxm_put_params);
  49.  
  50. /* The device descriptors */
  51.  
  52. /* define a subclass with useful state in it. */
  53. typedef struct lxm_device_s { /* a sub-class of gx_device_printer */
  54.     gx_device_common;
  55.     gx_prn_device_common;
  56.     int headSeparation;
  57. } lxm_device;
  58.  
  59. /* Standard lxm5700m device */
  60. lxm_device far_data gs_lxm5700m_device = {
  61.     prn_device_std_body(lxm_device, lxm5700m_procs, "lxm5700m",
  62.     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  63.     600, 600,    /* x dpi, y dpi */
  64.     0.2, 0.0, 0.0, 0.0,            /* margins */
  65.     1, lxm5700m_print_page),
  66.     16   /* default headSeparation value */
  67. };
  68.  
  69. /* I don't know the whole protocol for the printer, but let me tell
  70.  * you about the fraction I use.
  71.  * Each page begins with a header, which I describe as init1, init2, init3 --
  72.  *  (I separate them, because I've seen output in which those sections
  73.  *   seemed to appear independently, but I've deleted the only code I
  74.  *   had that actually used them separately.)
  75.  * Then there are a number of swipe commands, each of which describes one
  76.  * swipe of the printhead.  Each swipe command begins with a fixed
  77.  * header, which gives the number of bytes in the command,
  78.  * left and right margins,
  79.  * a vertical offset, some noise characters I don't know the meaning of,
  80.  * and the table of bits.  The bits are given one column at a time, using a
  81.  * simple compression scheme: a directory, consisting of two bytes, tells
  82.  * which sixteen-bit intervals of the 208-bit column contain 1-bits; then
  83.  * the appropriate number of sixteen-bit bit-maps follow.  (A zero-bit in the
  84.  * directory indicates that a bitmap for that sector follows.) In the worst case,
  85.  * this scheme would be bigger than the uncompressed bitmap, but it seems to
  86.  * usually save 80-90%.  The biggest complication of the bitmap scheme is this:
  87.  * There are two print-heads on the black cartridge, and they are 16 pixels
  88.  * (or headSeparation) apart.  Odd columns of the swipe address one printhead,
  89.  * and even columns the other.  On the following swipe, the printheads
  90.  * addressed by even and odd columns are reversed.  I think the printheads might be
  91.  * staggered, but the output I've seen staggers things in software;
  92.  * adjacent vertical bits on the same head are not addressed; the missing
  93.  * bits are written in by the second head when it passes (16 columns later.)
  94.  * In my code, I call the state of addressing one head or the other "direction".
  95.  * Originally I thought that the printhead was writing on both leftward and
  96.  * rightward motion, and that which head was addressed by odd columns changed
  97.  * accordingly.  I'm no longer sure this is true, but the head addressed by the
  98.  * even columns does alternate with each swipe.
  99.  */
  100. /*
  101.  * various output shorthands
  102.  */
  103.  
  104. #define init1() \
  105.     top(), \
  106.     0xA5,0, 3, 0x40,4,5, \
  107.     0xA5,0, 3, 0x40,4,6, \
  108.     0xA5,0, 3, 0x40,4,7, \
  109.     0xA5,0, 3, 0x40,4,8, \
  110.     0xA5,0, 4, 0x40,0xe0,0x0b, 3 
  111.  
  112. #define init2() \
  113.     0xA5,0, 11, 0x40,0xe0,0x41, 0,0,0,0,0,0,0, 2, \
  114.     0xA5,0, 6, 0x40, 5, 0,0,0x80,0 \
  115.  
  116. #define init3()  \
  117.     0x1b,'*', 7,0x73,0x30, \
  118.     0x1b,'*', 'm', 0, 0x14, 3, 0x84, 2, 0, 1, 0xf4, \
  119.     0x1b,'*', 7,0x63, \
  120.     0x1b,'*', 'm', 0, 0x42,  0, 0, \
  121.     0xA5,0, 5, 0x40,0xe0,0x80, 8, 7, \
  122.     0x1b,'*', 'm', 0, 0x40, 0x15, 7, 0x0f, 0x0f  \
  123.  
  124. #define top()  \
  125.     0xA5,0, 6, 0x40, 3,3,0xc0,0x0f,0x0f \
  126.  
  127. #define fin()  \
  128.     0x1b,'*', 7, 0x65 \
  129.  
  130.  
  131. #define outByte(b) putc(b, prn_stream)
  132.  
  133. #define RIGHTWARD 0
  134. #define LEFTWARD 1
  135. /* number of pixels between even columns in output and odd ones*/
  136. /* #define headSeparation 16 */
  137. /* overlap between successive swipes of the print head */
  138. #define overLap 104
  139. /* height of printhead in pixels */
  140. #define swipeHeight 208
  141. /* number of shorts described by each column directory */
  142. #define directorySize 13 
  143.  
  144. /* ------ Driver procedures ------ */
  145.  
  146.  
  147. /* Send the page to the printer. */
  148. private int
  149. lxm5700m_print_page(gx_device_printer *pdev, FILE *prn_stream)
  150. {    
  151.     int lnum,minX, maxX, i, l, highestX, leastX, extent;
  152.     int direction = RIGHTWARD;
  153.     int lastY = 0;
  154.     
  155.     int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
  156.     /* Note that in_size is a multiple of 8. */
  157.     int in_size = line_size * (swipeHeight);
  158.     int swipeBuf_size = in_size;
  159.     byte *buf1 = (byte *)gs_malloc(in_size, 1, "lxm_print_page(buf1)");
  160.     byte *swipeBuf =
  161.     (byte *)gs_malloc(swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
  162.     byte *in = buf1;
  163.  
  164.     /* Check allocations */
  165.     if ( buf1 == 0 || swipeBuf == 0 ) {
  166.     if ( buf1 ) 
  167. quit_ignomiously: /* and a goto into an if statement is pretty ignomious! */
  168.     gs_free((char *)buf1, in_size, 1, "lxm_print_page(buf1)");
  169.     if ( swipeBuf ) 
  170.         gs_free((char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
  171.     return_error(gs_error_VMerror);
  172.     }
  173.  
  174.     {    /* Initialize the printer and reset the margins. */
  175.     static const char init_string[] = {
  176.         init1(),
  177.         init2(),
  178.         init3()
  179.     };
  180.     fwrite(init_string, 1, sizeof(init_string), prn_stream);
  181.     }
  182.     /* Print lines of graphics */
  183.     for (lnum=0; lnum < pdev->height-swipeHeight ; ) { /* increment in body */
  184.     byte *in_data;
  185.     register byte *outp;
  186.     int lcnt;
  187.  
  188.     {    /* test for blank scan lines.  We  maintain the */
  189.         /* loop invariant lnum <pdev->height, but modify lnum */
  190.         int l;
  191.  
  192.         for (l=lnum; l<pdev->height; l++) {
  193.         /* Copy 1 scan line and test for all zero. */
  194.         gdev_prn_get_bits(pdev, l, in, &in_data);
  195.         if ( in_data[0] != 0 ||
  196.              memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
  197.              ) {
  198.             break;
  199.         }
  200.         }/* end for l */
  201.  
  202.         /* now l is the next non-blank scan line */
  203.         if (l >= pdev->height) {/* if there are no more bits on this page */
  204.         lnum = l;
  205.         break;            /* end the loop and eject the page*/
  206.         }
  207.  
  208.         /* leave room for following swipe to reinforce these bits */
  209.         if (l-lnum > overLap) lnum = l - overLap;
  210.  
  211.         /* if the first non-blank near bottom of page */
  212.         if (lnum >=pdev->height - swipeHeight) {
  213.         /* don't move the printhead over empty air*/
  214.         lnum = pdev->height - swipeHeight;
  215.         }
  216.     }
  217.  
  218.  
  219.     /* Copy the the scan lines. */
  220.     lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
  221.     if ( lcnt < swipeHeight ) {
  222.         /* Pad with lines of zeros. */
  223.         memset(in + lcnt * line_size, 0,
  224.            in_size - lcnt * line_size);
  225.     }
  226.  
  227.     /* compute right and left margin for this swipe */
  228.     minX = line_size;
  229.     maxX = 0;
  230.     for (l=0; l<swipeHeight; l++) {/* for each line of swipe */
  231.         for (i=0; i<minX; i++) {/* look for left-most non-zero byte*/
  232.         if (in[l*line_size+i] !=0) {
  233.             minX = i;
  234.             break;
  235.         }
  236.         }
  237.         for (i=line_size-1; i>=maxX; i--) {/* look for right-most */
  238.         if (in[l*line_size+i] !=0) {
  239.             maxX = i;
  240.             break;
  241.         }
  242.         }
  243.     }
  244.     minX = (minX&(-2)); /* truncate to even */
  245.     maxX = (maxX+3)&-2; /* raise to even */
  246.  
  247.     highestX = maxX*8-1;
  248.     leastX = minX*8;
  249.     extent = highestX -leastX +1;
  250.         
  251.     outp = swipeBuf;
  252.  
  253.     /* macro, not fcn call.  Space penalty is modest, speed helps */
  254. #define buffer_store(x) if(outp-swipeBuf>=swipeBuf_size) {\
  255.         gs_free((char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");\
  256.         swipeBuf_size*=2;\
  257.         swipeBuf = (byte *)gs_malloc(swipeBuf_size, 1, "lxm_print_page(swipeBuf)");\
  258.         if (swipeBuf == 0) goto quit_ignomiously;\
  259.         break;}\
  260.     else *outp++ = (x)
  261.  
  262.         {/* work out the bytes to store for this swipe*/
  263.  
  264.         int sx, sxBy8, sxMask;
  265.         int words[directorySize];
  266.         bool f, sum;
  267.         int retval=0;
  268.         int j,c,y;
  269.         int j1,c1;
  270.         int i,b,x, directory ;
  271.  
  272.         /* want to set up pointers for (upto two) stripes covered by the output*/
  273.  
  274.         /* now for each column covered by output: */
  275.         for (x=leastX; x<=highestX; x++) {
  276.             for (i=0; i<directorySize; i++) {
  277.             words[i] = 0;
  278.             }
  279.             directory = 0x2000;    /* empty directory != 0 */
  280.  
  281.             /* prime loops: make comparisons here */
  282.             switch (direction) {
  283.             case(RIGHTWARD):
  284.             sx = (x&1)==1 ? x : x-(((lxm_device*)pdev)->headSeparation);
  285.             j1 = (x&1);    /* even if x even, odd if x odd */
  286.             break;
  287.             default:    /* shouldn't happen ... but compilation checks */
  288.             case(LEFTWARD):
  289.             sx = (x&1)==0 ? x : x-((lxm_device*)pdev)->headSeparation;
  290.             j1 = 1-(x&1);    /* odd if x even, even if x odd */
  291.             }
  292.             c1 = 0x8000 >> j1;
  293.  
  294.             sxBy8 = sx/8;
  295.             sxMask = 0x80>>(sx%8);
  296.  
  297.             /* loop through all the swipeHeight bits of this column */
  298.             for (i = 0, b=1, y= sxBy8+j1*line_size; i < directorySize; i++,b<<=1) {
  299.             sum = false;
  300.             for (j=j1,c=c1 /*,y=i*16*line_size+sxBy8*/; j<16; j+=2, y+=2*line_size, c>>=2) {
  301.                 f = (in[y]&sxMask);
  302.                 if (f) {
  303.                 words[i] |= c;
  304.                 sum |= f;
  305.                 }
  306.             }
  307.             if (!sum) directory |=b;
  308.             }
  309.             retval+=2;
  310.             buffer_store(directory>>8); buffer_store(directory&0xff);
  311.             if (directory != 0x3fff) {
  312.             for (i=0; i<directorySize; i++) {
  313.                 if (words[i] !=0) {
  314.                 buffer_store(words[i]>>8) ; buffer_store(words[i]&0xff);
  315.                 retval += 2;
  316.                 }
  317.             }
  318.             }
  319.         }
  320. #undef buffer_store
  321.         }
  322.     {/* now write out header, then buffered bits */
  323.         int leastY = lnum;
  324.  
  325.         /* compute size of swipe, needed for header */
  326.         int sz = 0x1a + outp - swipeBuf;
  327.  
  328.         /* put out header*/
  329.         int deltaY = 2*(leastY - lastY);  /* vert coordinates here are 1200 dpi */
  330.         lastY = leastY;
  331.         outByte(0x1b); outByte('*'); outByte(3); 
  332.         outByte(deltaY>>8); outByte(deltaY&0xff);
  333.         outByte(0x1b); outByte('*'); outByte(4); outByte(0); outByte(0);
  334.         outByte(sz>>8); outByte(sz&0xff); outByte(0); outByte(3); 
  335.         outByte(1); outByte(1); outByte(0x1a);
  336.         outByte(0); 
  337.         outByte(extent>>8); outByte(extent&0xff); 
  338.         outByte(leastX>>8); outByte(leastX&0xff);
  339.         outByte(highestX>>8); outByte(highestX&0xff);
  340.         outByte(0); outByte(0);
  341.         outByte(0x22); outByte(0x33); outByte(0x44); 
  342.         outByte(0x55); outByte(1);
  343.         /* put out bytes */
  344.         fwrite(swipeBuf,1,outp-swipeBuf,prn_stream);
  345.     }
  346.         lnum += overLap;
  347.         direction ^= 1;
  348.     }/* ends the loop for swipes of the print head.*/
  349.  
  350.     /* Eject the page and reinitialize the printer */
  351.     {
  352.     static const char bottom[] = {
  353.         fin() /*,  looks like I can get away with only this much ...
  354.         init1(),
  355.         init3(),
  356.         fin()   , 
  357.         top(),
  358.         fin()  */
  359.     };
  360.     fwrite(bottom, 1, sizeof(bottom), prn_stream);
  361.     }
  362.     fflush(prn_stream);
  363.  
  364.     gs_free((char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
  365.     gs_free((char *)buf1, in_size, 1, "lxm_print_page(buf1)");
  366.     return 0;
  367. }
  368.  
  369. /* 
  370.  * There are a number of parameters which can differ between ink cartridges. 
  371.  * The Windows driver asks you to recalibrate every time you load a new
  372.  * cartridge.
  373.  * most of the parameters adjusted there relate to color, and so this 
  374.  * monotone driver doesn't need them.  However, the Lexmark 5700 black
  375.  * cartridge has two columns of dots, separated by about 16 pixels.
  376.  * This `head separation' distance can vary between cartridges, so
  377.  * we provide a parameter to set it.  In my small experience I've not
  378.  * set the corresponding parameter in windows to anything greater than 17
  379.  * or smaller than 15, but it would seem that it can vary from 1 to 32,
  380.  * based on the calibration choices offered.
  381.  *
  382.  * As I understand the rules laid out in gsparams.h,
  383.  * lxm_get_params is supposed to return the current values of parameters
  384.  * and lxm_put_params is supposed to set up values in the lxm_device
  385.  * structure which can be used by the lxm5700m_print_page routine.
  386.  * I've copied my routines from gdevcdj.c
  387.  */
  388.  
  389. private int
  390. lxm_get_params(gx_device *pdev, gs_param_list *plist)
  391. {       
  392.     lxm_device* const ldev = (lxm_device*)pdev;
  393.     int code = gdev_prn_get_params(pdev, plist);
  394.  
  395.     if ( code < 0 ) return code;
  396.     code = param_write_int(plist, 
  397.                "HeadSeparation",
  398.                (int *)&(ldev->headSeparation));
  399.            
  400.     return code;
  401. }
  402.  
  403. /* put_params is supposed to check all the parameters before setting any. */
  404. private int
  405. lxm_put_params(gx_device *pdev, gs_param_list *plist)
  406. {
  407.     int ecode;
  408.     lxm_device* const ldev = (lxm_device*)pdev;
  409.     int trialHeadSeparation=ldev->headSeparation;
  410.     int code = param_read_int(plist, "HeadSeparation", &trialHeadSeparation);
  411.  
  412.     if ( trialHeadSeparation < 1 || trialHeadSeparation > 32 )
  413.     param_signal_error(plist, "HeadSeparation", gs_error_rangecheck);
  414.     /* looks like param_signal_error is not expected to return */
  415.     ecode = gdev_prn_put_params(pdev, plist);    /* call super class put_params */
  416.     if ( code < 0 ) return code;
  417.     if (ecode < 0) return ecode;
  418.  
  419.     /* looks like everything okay; go ahead and set headSeparation */
  420.     ldev->headSeparation = trialHeadSeparation;
  421.     if ( code == 1) return ecode; /* I guess this means there is no "HeadSeparation" parameter */
  422.     return 0;
  423. }
  424.